#ifndef _BASE
#define _BASE

// INCLUDES --------------------------------------------------------------------------
#include "stdbool.h"
#include "string.h"
#include "stdio.h"
#include "math.h"

// TYPEDEFS --------------------------------------------------------------------------

// float
typedef float              fp32;
typedef double             fp64;

// byte
typedef char               b8;
typedef unsigned char      ub8;

// single
typedef short              i16;
typedef unsigned short     ui16;

// int
typedef int                i32;
typedef unsigned int       ui32;

// long
typedef long               i64;
typedef unsigned long      ui64;

// long long
typedef long long          i128;
typedef unsigned long long ui128;

// DEFINES ---------------------------------------------------------------------------
#define DEBUG

#ifdef DEBUG
  // ------------------------------------------------------------------
	/* msg -> message string to print
	   ... -> alternative string variables */
	#define PRINT(msg, ...) \
	{ \
		printf((msg), __VA_ARGS__); \
	};

	// sanity check(s) for various errors

  /* f   -> function name as string
     p   -> pointer to check
     ... -> alternative return value */
  #define GUARD_NULL(f, p, ...) \
  { \
    if ((p) == NULL) \
    { \
      PRINT( "NULL pointer as target! [%s->%s:%d]\n", (f), __FILE__, __LINE__ ); \
      return __VA_ARGS__; \
    } \
  }

  /* f   -> function name as string
     k   -> value to check
     ... -> alternative return value */
  #define GUARD_ZERO(f, k, ...) \
  { \
    if ((k) == 0) \
    { \
      PRINT( "division by zero! [%s->%s:%d]\n", (f), __FILE__, __LINE__ ); \
      return __VA_ARGS__; \
    } \
  }
  // ------------------------------------------------------------------
#else // DEBUG
  // ------------------------------------------------------------------
  // no performance penalty here...

	#define PRINT(msg, ...) {};
	
  #define GUARD_NULL(f, p, ...) { return __VA_ARGS__; };
  #define GUARD_ZERO(f, k, ...) { return __VA_ARGS__; };
  // ------------------------------------------------------------------
#endif // DEBUG

// CLAMP -----------------------------------------------------------------------------

/* Clamp to: [a,b]
   v   -> value to clamp
   min -> min value for v
   max -> max value for v */
#define CLAMP(v, min, max) \
{ \
  if      ((v) < (min)) (v) = (min); \
  else if ((v) > (max)) (v) = (max); \
}

/* Clamp to: [a,b[
   v   -> value to clamp
   min -> min value for v */
#define CLAMP_LOW(v, min) { if ((v) < (min)) v = (min); }

/* Clamp to: ]a,b]
   v   -> value to clamp
   max -> max value for v */
#define CLAMP_HIGH(v, max) { if ((v) > (max)) v = (max); }

// STRING GENERATOR ------------------------------------------------------------------
#define MAX_STRING_GEN_SIZE 20000         // max size of string generator buffer
static b8 stringGen[MAX_STRING_GEN_SIZE]; // global string generator buffer
static int stringGenSuccess = -1;         // global snprintf status after STRING_GEN

/* String Generator
   f   -> format string
   ... -> alternative string variables

   get return value of snprintf(),
   check for return value being non-negative and smaller than buffer size,
   
   if yes: assembling of string to the buffer succeeded
   in case of error: buffer will contain an empty string
*/
#define STRING_GEN(f, ...) \
{ \
  stringGenSuccess = snprintf(stringGen, MAX_STRING_GEN_SIZE, (f), __VA_ARGS__); \
  if (stringGenSuccess < 0 || stringGenSuccess >= MAX_STRING_GEN_SIZE) \
  { \
    memset(stringGen, 0, MAX_STRING_GEN_SIZE); \
  } \
};

#endif // _BASE
